05. While 循环

While 循环

For 循环是 "定迭代" 的一种,它表示循环主体将执行指定次数。列表的 for 循环针对列表中的每个元素执行一次主体。使用 range 函数的 for 循环,其执行次数将由 range 函数调用指定。
这种迭代与 "不定迭代" 不同,后者指循环重复未知次数,直到满足某些条件时循环才会结束。下面这个示例将模拟二十一点发牌,使用 while 循环将卡片牌堆拖到手牌中,在手牌的值大于或等于 17 时停止。

card_deck = [4, 11, 8, 5, 13, 2, 8, 10]
hand = []

while sum(hand) <= 17:
    hand.append(card_deck.pop())

print(hand)

这个示例中有一个新的函数 sum 和一个新的列表方法 pop sum 非常直观,它用于计算列表中元素的总和。 pop 方法与 append 方法相反, pop 从列表中移除一个元素并返回它。大家可以在 官方文档 中阅读更多关于 pop 方法的内容。

下面我们来看一下 while 循环的语法:

  1. 关键字 While 表示这是一个 while 循环
  2. 接下来是一个测试表达式,在该示例中表达式为 sum(hand) <= 21 。如果表达式为真,将执行循环主体,之后将再次判断该表达式是否为真。这一过程重复判断测试表达式的真假,并运行循环主体,直到表达式变为 false。
  3. 循环主体前需要缩进四个空格。循环主体应该以某种方式修改测试表达式中的变量。如果测试表达式的值没有改变,这将导致一个无限循环!在上面的示例中,循环主体将数字追加到了 hand 列表中,因此增加了 sum(hand) 的值。

练习:最大平方数

请在下面的练习中完成 nearest_square 函数。该函数取一个整数参数 limit ,并返回一个小于 limit 的最大平方数。平方数是整数乘以自身的乘积,例如 6*6 等于 36,所以 36 是一个平方数。

这个代码有多种编写方法,但是我建议大家使用一个 while 循环!

下面是一个测试用例,在函数编写完成后,你可以使用它来测试自己的代码。当然你也可以随意使用其他数据进行测试。

test1 = nearest_square(40)
print("expected result: 36, actual result: {}".format(test1))

Start Quiz:

#TODO: Implement the nearest_square function

break 停止

For 循环迭代序列中的每个元素,而 while 循环在满足条件时停止迭代。在大多数情况下,这两种用法就已经足够了,但我们有时候需要更精确地控制循环何时结束。这时我们就需要使用关键字 break 了。

循环在遇到 break 时便会立即停止。如果检测到已经满足某些条件,那么我们可以使用这些条件来结束循环。在 for 循环和 while 循环中,我们都可以使用 break

我们将通过下面的示例学习如何使用 break
假设你想按照一个货物列表来装载货船。每个货物都具有一定的重量,但货船有最大载重限制。在理想情况下,我们希望能够装载所有货物,同时避免货船超载。因此,当货物达到货船的额定载重时,应停止装载。所以在这里我们将使用一个 for 循环,来记录装载的货物以及总重量。一旦所装载的货物达到了货船的最大载重,应使用 break 语句停止装载。

注意: 下方示例中的 manifest 是一个由列表组成的列表。我们以前接触过列表,也知道有些列表元素本身就是列表,而 manifest 变量就是这种情况。 manifest 列表中的每个元素本身就是一个列表,它包含两个元素:货物名称和货物重量。

# each item in the manifest is an item and its weight
manifest = [["bananas", 15], ["mattresses", 34], ["dog kennels",42], ["machine that goes ping!", 120], ["tea chests", 10], ["cheeses", 0]]

cargo_weight = 0
cargo_hold = []

for cargo in manifest:
    if cargo_weight >= 100:
        break
    else:
        cargo_hold.append(cargo[0])
        cargo_weight += cargo[1]

上方这段代码旨在避免船上货物的重量超过规定的限值 100。现在我们来检查一下装载在船上的货物重量。

>>> print(cargo_weight)
211
>>> print(cargo_hold)
['bananas', 'mattresses', 'dog kennels', 'machine that goes ping!']

上方的结果好像不太对劲,货船装载的货物严重超过了重量限制。尽管 break 语句阻止了我们将每一件货物都放在船上,但最后仍然超过了限制。

单看上方的代码我们很难判断哪里出了问题。在这里我们可以在代码中使用 print 函数来查看问题出在哪里。这是一个非常方便的方法,因为随着代码的逐步运行,它可以让我们深入了解数据状态。如果打印的提示正确(并给出上下文),这可能有助于我们找到错误。

我们在下方的代码中添加了调试语句:

cargo_weight = 0
cargo_hold = []

for cargo in manifest:
    print("debug: the weight is currently: {}".format(cargo_weight))
    if cargo_weight >= 100:
        print("debug: breaking loop now!")
        break
    else:
        print("debug: adding item: {}".format(cargo[0]))
        print("debug: with weight: {}".format(cargo[1]))
        cargo_hold.append(cargo[0])
        cargo_weight += cargo[1]

以下是带标注循环的输出:

debug: the weight is currently: 0
debug: adding item: bananas
debug: with weight: 15
debug: the weight is currently: 15
debug: adding item: mattresses
debug: with weight: 34
debug: the weight is currently: 49
debug: adding item: dog kennels
debug: with weight: 42
debug: the weight is currently: 91
debug: adding item: machine that goes ping!
debug: with weight: 120
debug: the weight is currently: 211
debug: breaking loop now!

通过这些调试日志,我们可以看到循环正确地将货物添加到货船中,但是在满足重量限制之后,还增加了一个额外货物。

思考

QUESTION:

大家知道为什么列表中添加了一个额外的货物吗?请告诉我们你的想法,并查看我们的想法。

ANSWER:

感谢大家思考!问题是导致循环 break if 条件只有在 当前 重量超过限制时才会触发。相反,应该检查添加下一个货物后的 重量是否超过限制。

这段新代码将解决问题:

for cargo in manifest:
    if cargo_weight + cargo[1] >= 100:
        break
    else:
        cargo_hold.append(cargo[0])
        cargo_weight += cargo[1]

可以再次添加 print 语句,并看看其如何运行。

练习:断开字符串

请在下方的练习中使用 break 语句来编写自己的循环。你的任务是创建一个长度为 140 个字符的字符串 news_ticker 。你可以从 headlines 列表添加标题,并在每个标题之间插入一个空格。当 news_ticker 的长度超过 140 个字符时,你可以从中间截断最后一个标题,使字符长度保持在 140。

请记住,在 for 循环和 while 循环中均可使用 break 。请在下方练习中使用你心目中最合适的循环,你也可以考虑在代码中添加 print 语句来帮助你解决错误。

Start Quiz:

headlines = ["Local Bear Eaten by Man",
             "Legislature Announces New Laws",
             "Peasant Discovers Violence Inherent in System",
             "Cat Rescues Fireman Stuck in Tree",
             "Brave Knight Runs Away",
             "Papperbok Review: Totally Triffic"]

news_ticker = ""
# TODO: set news_ticker to a string that contains no more than 140 characters long.
# HINT: modify the headlines list to verify your loop works with different inputs